/*
 * Copyright 2009 Eat Local Food, LLC
 *
 * This file is part of gwtCommerce.
 *
 * gwtCommerce is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * gwtCommerce is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with gwtCommerce.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.elf.gwt.common.client.widget;

import java.util.Iterator;
import java.util.List;

import com.elf.gwt.common.client.GWTCommerceCommonConstants;
import com.elf.gwt.common.client.callback.TableNavigatorWidgetCallback;
import com.elf.gwt.common.client.model.OrderbyModel;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.InlineHTML;
import com.google.gwt.user.client.ui.InlineLabel;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.PushButton;
import com.google.gwt.user.client.ui.Widget;

/**
 *  A table navigator allows for easy pagination of a table.
 */
public class TableNavigatorWidget extends HorizontalPanel
{
	/** classname without package used for styling **/
	protected String handle;
	/** type of record in table **/
	protected String type;
	/** total rows in dataset **/
	protected int totalRows;
	/** maximum rows per page **/
	protected int maxRows;
	/**  actual rows in current page **/
	protected int rows;
	/** number of rows into dataset starts at zero **/
	protected int offset;
	/** max number of pages per window **/
	protected int maxPageLinks;
	/** Sort order drop down **/
	protected List<OrderbyModel> orderbyModels;
	/** Constants for text **/
	protected GWTCommerceCommonConstants constants;
	/** Constant used to find images **/
	protected String languageCode;
	/** callback when an event occurs **/
	protected TableNavigatorWidgetCallback callback;

	/** pages are grouped into windows **/
	protected int currentWindow;
	/** the maximum number of page groups (windows) **/
	protected int maxWindow;
	
	protected String imageDirectory = "";

	//widgets
	protected InlineLabel descPanel = null;
	protected ListBox orderbyListBox = null;
	protected HorizontalPanel prevNextPanel = null;
	protected PushButton firstButton = null;
	protected PushButton prevButton = null;
	protected PushButton nextButton = null;
	protected PushButton lastButton = null;
	
	/**
	 * Constructor
	 */
	public TableNavigatorWidget(
			String type, 
			int totalRows, 
			int maxRows, 
			int rows, 
			int offset, 
			int maxPageLinks, 
			List<OrderbyModel> orderbyModels, 
			GWTCommerceCommonConstants constants, 
			String languageCode,
			TableNavigatorWidgetCallback callback,
			String imageDirectory)
	{
		this.type = type;
		this.totalRows = totalRows;
		this.maxRows = maxRows;
		this.rows = rows;
		this.offset = offset;
		this.maxPageLinks = maxPageLinks;
		this.orderbyModels = orderbyModels;
		this.constants = constants;
		this.languageCode = languageCode;
		this.callback = callback;
		this.imageDirectory = imageDirectory;

		//calculate handle
		String handle = this.getClass().getName();
		int index = handle.lastIndexOf(".");
		if (index > -1) handle = handle.substring(index+1);
		this.handle = handle;

		refresh(true);
	}

	//redraw with less churn on widgets.
	public void refresh(
			int totalRows, 
			int maxRows, 
			int rows, 
			int offset)
	{
		this.totalRows = totalRows;
		this.maxRows = maxRows;
		this.rows = rows;
		this.offset = offset;
		
		refresh(false);
	}
		
	/**
	 * setup of component.
	 * @param initialize
	 */
	public void refresh (boolean initialize)
	{
		if (initialize || orderbyListBox == null)
		{
			this.clear();
			//page description panel
			descPanel = new InlineLabel();
			this.add(descPanel);

			//order by dropdown
			if (orderbyModels != null && orderbyModels.size() > 0 && totalRows > 1)
			{
				orderbyListBox = new ListBox();
				orderbyListBox.addChangeHandler(orderbyChangeHandler);
				add(orderbyListBox);
				setCellHorizontalAlignment(orderbyListBox, HasHorizontalAlignment.ALIGN_CENTER);
			}

			//previous next panel
			prevNextPanel = new HorizontalPanel();
			add(prevNextPanel);

			//widths
			if (orderbyModels != null && orderbyModels.size() > 0 && totalRows > 1)
			{	
				setCellWidth(descPanel, "34%");
				setCellWidth(orderbyListBox, "33%");
				setCellWidth(prevNextPanel, "33%");
			}
			else
			{
				setCellWidth(descPanel, "50%");
				setCellWidth(prevNextPanel, "50%");
			}
			setCellHorizontalAlignment(prevNextPanel, HasHorizontalAlignment.ALIGN_RIGHT);
		}

		//page description panel
		int start = offset + 1;
		if (rows == 0) start = offset;

		String descString = 
			constants.TableNavigatorWidgetDisplaying() + " " + (start) + 
			" " + constants.TableNavigatorWidgetTo() + " " + (offset + rows) + 
			" " + constants.TableNavigatorWidgetOf() + " " + totalRows + 
			" " + type + constants.TableNavigatorWidgetEnd();

		descPanel.setText(descString);

		//order by dropdown
		if (orderbyModels != null && orderbyModels.size() > 0 && totalRows > 1)
		{
			if (orderbyListBox != null) orderbyListBox.clear();
			Iterator<OrderbyModel> iter = orderbyModels.iterator();
			int i = 0;
			while (iter.hasNext())
			{
				OrderbyModel orderbyModel = iter.next();
				orderbyListBox.addItem(orderbyModel.getLabel(),orderbyModel.getOrderclause());
				if (orderbyModel.isSelected()) {
					orderbyListBox.setItemSelected(i, true);
				}
				else {
					orderbyListBox.setItemSelected(i, false);
				}
				i++;
			}
		}

		int totalPages = (int)Math.ceil((1.0 * totalRows) / (1.0 * maxRows));
		int currentPage = (int)Math.ceil((1.0 * (offset+1)) / (1.0 * maxRows));

		prevNextPanel.clear();
		//First and Previous
		if (currentPage > 2)
		{
			if (firstButton == null)
			{
				firstButton = new PushButton(new Image(getImageCommonDirectory() + "go-first.png"), new Image(getImageCommonDirectory() + "go-first_pushed.png"), firstClickHandler);
				firstButton.setStyleName(getHandle() + "FirstButton");
				firstButton.addStyleName("ComponentLink");
				firstButton.addStyleName("pageLink");
			}
			prevNextPanel.add(new InlineHTML("&nbsp;"));
			prevNextPanel.add(firstButton);
			prevNextPanel.add(new InlineHTML("&nbsp;"));
		}
		else
		{
			prevNextPanel.add(new InlineHTML("&nbsp;"));
			prevNextPanel.add(new Image(getImageCommonDirectory() + "go-first-grey.png"));
			prevNextPanel.add(new InlineHTML("&nbsp;"));
		}

		if (currentPage > 1)
		{
			if (prevButton == null)
			{
				prevButton = new PushButton(new Image(getImageCommonDirectory() + "go-previous.png"), new Image(getImageCommonDirectory() + "go-previous_pushed.png"), prevClickHandler);
				prevButton.setStyleName(getHandle() + "PrevButton");
				prevButton.addStyleName("ComponentLink");
				prevButton.addStyleName("pageLink");
			}
			prevNextPanel.add(new InlineHTML("&nbsp;"));
			prevNextPanel.add(prevButton);
			prevNextPanel.add(new InlineHTML("&nbsp;"));
		}
		else
		{
			prevNextPanel.add(new InlineHTML("&nbsp;"));
			prevNextPanel.add(new Image(getImageCommonDirectory() + "go-previous-grey.png"));
			prevNextPanel.add(new InlineHTML("&nbsp;"));
		}


		//Windows (group of pages)

		//check if number_of_pages > $max_page_links
		currentWindow = (int)Math.ceil((1.0 * currentPage) / (1.0 * maxPageLinks));
		maxWindow = (int)Math.ceil((1.0 * totalPages) / (1.0 * maxPageLinks));

		//previous window of pages
		if (currentWindow > 1)
		{
			InlineLabel prevWindowLabel = new InlineLabel("...");
			prevWindowLabel.addClickHandler(prevWindowClickHandler);
			prevWindowLabel.addStyleName("pageLink");
			prevWindowLabel.addStyleName("ComponentLink");
			prevNextPanel.add(new InlineHTML("&nbsp;"));
			prevNextPanel.add(prevWindowLabel);
			prevNextPanel.add(new InlineHTML("&nbsp;"));
		}

		//page nn button
		if (totalPages > 1)
		{
			for (int jumpToPage = 1 + ((currentWindow - 1) * maxPageLinks); 
			(jumpToPage <= (currentWindow * maxPageLinks)) && (jumpToPage <= totalPages); 
			jumpToPage++) 
			{
				InlineLabel pageLabel = new InlineLabel(" " + jumpToPage + " ");
				if (jumpToPage != currentPage) {
					pageLabel.addStyleName("pageLink");
					pageLabel.addStyleName("ComponentLink");
					pageLabel.addStyleName("page:" + jumpToPage);
					pageLabel.addClickHandler(pageClickHandler);
				} 
				prevNextPanel.add(new InlineHTML("&nbsp;"));
				prevNextPanel.add(pageLabel);
				prevNextPanel.add(new InlineHTML("&nbsp;"));
			}
		}

		//next window of pages
		if (currentWindow < maxWindow)
		{
			InlineLabel nextWindowLabel = new InlineLabel("...");
			nextWindowLabel.addClickHandler(nextWindowClickHandler);
			nextWindowLabel.addStyleName("pageLink");
			nextWindowLabel.addStyleName("ComponentLink");
			prevNextPanel.add(new InlineHTML("&nbsp;"));
			prevNextPanel.add(nextWindowLabel);
			prevNextPanel.add(new InlineHTML("&nbsp;"));
		}

		//Next and Last
		if (currentPage < totalPages)
		{
			if (nextButton == null)
			{
				nextButton = new PushButton(new Image(getImageCommonDirectory() + "go-next.png"), new Image(getImageCommonDirectory() + "go-next_pushed.png"), nextClickHandler);
				nextButton.setStyleName(getHandle() + "NextButton");
				nextButton.addStyleName("ComponentLink");
				nextButton.addStyleName("pageLink");
			}
			prevNextPanel.add(new InlineHTML("&nbsp;"));
			prevNextPanel.add(nextButton);
			prevNextPanel.add(new InlineHTML("&nbsp;"));
		}
		else
		{
			prevNextPanel.add(new InlineHTML("&nbsp;"));
			prevNextPanel.add(new Image(getImageCommonDirectory() + "go-next-grey.png"));
			prevNextPanel.add(new InlineHTML("&nbsp;"));
		}

		if (currentPage+1 < totalPages)
		{
			if (lastButton == null)
			{
				lastButton = new PushButton(new Image(getImageCommonDirectory() + "go-last.png"), new Image(getImageCommonDirectory() + "go-last_pushed.png"), lastClickHandler);
				lastButton.setStyleName(getHandle() + "LastButton");
				lastButton.addStyleName("ComponentLink");
				lastButton.addStyleName("pageLink");
			}
			prevNextPanel.add(new InlineHTML("&nbsp;"));
			prevNextPanel.add(lastButton);
			prevNextPanel.add(new InlineHTML("&nbsp;"));
		}
		else
		{
			prevNextPanel.add(new InlineHTML("&nbsp;"));
			prevNextPanel.add(new Image(getImageCommonDirectory() + "go-last-grey.png"));
			prevNextPanel.add(new InlineHTML("&nbsp;"));
		}
	}

	protected ClickHandler firstClickHandler = new ClickHandler() {
		public void onClick(ClickEvent event) {
			callback.onSelection(0);
		}
	};

	protected ClickHandler prevClickHandler = new ClickHandler() {
		public void onClick(ClickEvent event) {
			callback.onSelection(offset - maxRows);
		}
	};

	protected ClickHandler prevWindowClickHandler = new ClickHandler() {
		public void onClick(ClickEvent event) {
			int newOffset = ((((currentWindow -1) * maxPageLinks) -1) * maxRows);
			callback.onSelection(newOffset);
		}
	};

	protected ClickHandler nextWindowClickHandler = new ClickHandler() {
		public void onClick(ClickEvent event) {
			int newOffset = ((currentWindow * maxPageLinks) * maxRows);
			callback.onSelection(newOffset);
		}
	};

	protected ClickHandler pageClickHandler = new ClickHandler() {
		public void onClick(ClickEvent event) {
			Widget source = (Widget)event.getSource();
			String strPage = source.getStyleName();
			strPage = strPage.substring(strPage.indexOf("page:") + 5).trim();
			int index = strPage.indexOf(" ");
			if (index > -1) strPage = strPage.substring(0, index);
			int page = Integer.parseInt(strPage);
			callback.onSelection((page -1) * maxRows);
		}
	};

	protected ClickHandler nextClickHandler = new ClickHandler() {
		public void onClick(ClickEvent event) {
			callback.onSelection(offset + maxRows);
		}
	};

	protected ClickHandler lastClickHandler = new ClickHandler() {
		public void onClick(ClickEvent event) {
			int totalPages = (int)Math.ceil((1.0 * totalRows) / (1.0 * maxRows));
			callback.onSelection((totalPages -1) * maxRows);
		}
	};

	protected ChangeHandler orderbyChangeHandler = new ChangeHandler() {
		public void onChange(ChangeEvent event) {
			ListBox widget = (ListBox)event.getSource();
			callback.onOrderby(widget.getValue(widget.getSelectedIndex()));
		}
	};

	public String getHandle()
	{
		return handle;
	}

	public String getImageCommonDirectory()
	{
		return imageDirectory + "common/" + languageCode + "/";
	}
}
